import os
import sys
import shutil
import pyttsx3
from moviepy.editor import AudioClip,AudioFileClip,VideoFileClip
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from widgets.input_str_widget import InputStr

class ConvertProgress(QWidget):
    def __init__(self):
        super(ConvertProgress,self).__init__()
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowMinimizeButtonHint)
        self.setMinimumWidth(500)
        self.mainLayout=QVBoxLayout()
        self.mainLayout.setContentsMargins(15,12,15,15)
        self.tip_label=QLabel("生成音频：")
        self.tip_label.setAlignment(Qt.AlignCenter)
        self.progress_layout=QHBoxLayout()
        self.p=QProgressBar()

        self.mainLayout.addWidget(self.tip_label)
        self.mainLayout.addWidget(self.p)
        self.setLayout(self.mainLayout)

    def setValue(self,value):
        self.p.setValue(value)


class ConvertAudipClip(QThread):

    def __init__(self,engine,clips_path,paragraph_list,max_dir):# videopath,
        super(ConvertAudipClip, self).__init__()
        self.engine=engine
        self.clips_path = clips_path
        self.paragraph_list=paragraph_list
        self.paragraph_len=len(self.paragraph_list)
        if self.paragraph_len==1:
            self.paragraph_len=2
        self.max_dir=max_dir
        self.count=0
        self.percent=0


    def run(self):
        for i in self.paragraph_list:
            per_clipdir=os.path.join(self.clips_path,'{}'.format(self.count+self.max_dir))

            if not os.path.exists(per_clipdir):
                os.makedirs(per_clipdir)
            mp3_path=os.path.join(per_clipdir,'clip.mp3')
            txt_path=os.path.join(per_clipdir,'clip.txt')
            self.engine.save_to_file(i, mp3_path)
            self.engine.runAndWait()
            clip_mp3=AudioFileClip(mp3_path)
            duration=clip_mp3.duration
            data={'duration':duration,'subtitle':i,'atype':'clip','mp3':mp3_path,'txt':txt_path}
            with open(txt_path,'w',encoding='utf8') as w:
                w.write(str(data))

            self.count+=1
            self.percent = int(self.count * 100.0 / (self.paragraph_len - 1))



class AudioClipList(QListWidget):
    clip_dir_Signal=pyqtSignal(str)
    def __init__(self,audioClips_path):
        super(AudioClipList, self).__init__()

        self.audioClips_path=audioClips_path
        # self.setMinimumHeight(250)
        # self.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.setContextMenuPolicy(Qt.CustomContextMenu)  # 开启自定义右击菜单
        self.customContextMenuRequested.connect(self.right_menu)
        self.setMaximumWidth(250)
        self.init_default()
        self.itemClicked.connect(self._show_data)

    def init_default(self):
        clips_dir_list=os.listdir(self.audioClips_path)
        for i in clips_dir_list:
            item=QListWidgetItem()
            item.setText(i)
            item.setTextAlignment(Qt.AlignCenter)
            item.setForeground(QBrush(QColor('#FF8C00')))
            self.addItem(item)

    def _auto_bgm_dir(self):
        """
        自动获取当前bgm的合适索引
        :return:
        """
        bmg_path=os.path.join(self.audioClips_path,'bgm')
        bgm_dirs=os.listdir(bmg_path)
        max_num=0
        if bgm_dirs:
            int_bgm_dirs=[int(d) for d in bgm_dirs]
            max_num=max(int_bgm_dirs)+1
        auto_dir=os.path.join(bmg_path,str(max_num))
        return auto_dir

    def right_menu(self):
        rMenu = QMenu()
        adddAction=QAction(u'添加音频目录',self)
        adddAction.triggered.connect(self._add_clipDir)

        importBgmAction=QAction(u'导入背景音乐',self)
        importBgmAction.triggered.connect(self._import_bgm)

        deleteAction=QAction(u'删除音频目录',self)
        deleteAction.triggered.connect(self._del_clipDir)

        rMenu.addAction(adddAction)
        rMenu.addAction(importBgmAction)
        rMenu.addAction(deleteAction)
        rMenu.exec_(QCursor.pos())

    def _add_clipDir(self):
        self.input_window=InputStr('目录名称')
        self.input_window.content_edit_Signal.connect(self._create_clip_dir)
        self.input_window.show()

    def _import_bgm(self):
        """
        导入背景音乐
        :return:
        """
        if self.audioClips_path:
            fileName = QFileDialog.getOpenFileName(None, "选择音乐", ".", "(*.mp3;*.MP3;*.wav;*.WAV;*.mp4;*.MP4;*.avi;*.AVI)")
            print(fileName)
            if fileName[0]:
                bgmpath=fileName[0]
                # print('bgmpath:',bgmpath)
                dir_path,file_name=os.path.split(bgmpath)
                m_name,m_type=os.path.splitext(file_name)
                if m_type in ['.mp4','.MP4','.avi','.AVI']:   #如果是视频格式，那么就是提取音频
                    print('自动提取音频',bgmpath)
                    v_clip=VideoFileClip(bgmpath)
                    a_clip=v_clip.audio
                    if a_clip:
                        duration=a_clip.duration()
                        new_bgm_dir=self._auto_bgm_dir()
                        if not os.path.exists(new_bgm_dir):
                            os.makedirs(new_bgm_dir)
                        new_bgm=os.path.join(new_bgm_dir,'clip.mp3')
                        txt = os.path.join(new_bgm_dir, 'clip.txt')
                        a_clip.write_audiofile(new_bgm)
                        data={}
                        data['duration']=duration
                        data['atype']='bgm'
                        data['mp3']=new_bgm
                        data['subtitle']=m_name
                        data['txt']=txt
                        with open(txt,'w',encoding='utf8') as w:
                            w.write(str(data))

                    else:
                        QMessageBox.information(None,'错误','该视频不能自动提取音频')
                else:  #如果是音频，格式那么直接copy并生成音频信息
                    print('解析音频',bgmpath)
                    try:
                        a_clip=AudioFileClip(bgmpath)
                        if a_clip:
                            duration=a_clip.duration
                            new_bgm_dir = self._auto_bgm_dir()
                            if not os.path.exists(new_bgm_dir):
                                os.makedirs(new_bgm_dir)
                            new_bgm = os.path.join(new_bgm_dir, 'clip.mp3')
                            txt = os.path.join(new_bgm_dir, 'clip.txt')
                            shutil.copyfile(bgmpath, new_bgm)
                            data = {}
                            data['duration'] = duration
                            data['atype'] = 'bgm'
                            data['mp3'] = new_bgm
                            data['subtitle'] = m_name
                            data['txt'] = txt
                            with open(txt, 'w', encoding='utf8') as w:
                                w.write(str(data))
                        else:
                            QMessageBox.information(None, '错误', '该音频无法解析')
                    except Exception as _import_bgm_ERR:
                        print('_import_bgm_ERR:',str(_import_bgm_ERR))

    def _del_clipDir(self):
        current_item=self.currentItem()
        print(current_item)
        if current_item:
            text=current_item.text()
            if text not in ['bgm','default']:
                result=QMessageBox.question(self,'提示','是否删除该目录所有内容',QMessageBox.Yes|QMessageBox.No,QMessageBox.No)
                if result==QMessageBox.Yes:
                    dir_path=os.path.join(self.audioClips_path,text)
                    shutil.rmtree(dir_path)
            else:
                result=QMessageBox.question(self,'提示','清空预设目录下所有内容',QMessageBox.Yes|QMessageBox.No,QMessageBox.No)
                if result==QMessageBox.Yes:
                    dir_path=os.path.join(self.audioClips_path,text)
                    files=os.listdir(dir_path)
                    if files:
                        try:
                            for f in files:
                                file_path=os.path.join(dir_path,f)
                                os.remove(file_path)
                        except Exception as remove_file_ERR:
                            print(str(remove_file_ERR))

    def _create_clip_dir(self,dir_name):
        new_dir=os.path.join(self.audioClips_path,dir_name)
        if not os.path.exists(new_dir):
            os.makedirs(new_dir)
            item=QListWidgetItem()
            item.setText(dir_name)
            item.setTextAlignment(Qt.AlignCenter)
            self.addItem(item)

    def _show_data(self,item):
        text=item.text()
        print(text)
        self.clip_dir_Signal.emit(text)


class SubtitleText(QTextEdit):
    def __init__(self):
        super(SubtitleText, self).__init__()

    def keyPressEvent(self, QKeyEvent):
        if QKeyEvent.key() ==Qt.Key_QuoteLeft:
            self.textCursor().insertText('[<end>]')
        
        super(SubtitleText, self).keyPressEvent(QKeyEvent)

class PlayThread(QThread):

    def __init__(self,engine,text):
        super(PlayThread, self).__init__()
        self.engine=engine
        self.text=text
        # self.stop=0
    def run(self):
        self.engine.say(self.text)
        self.engine.runAndWait()
        # if self.stop:
        #     self.engine.stop()

    def stop(self,b):
        self.stop=b

class SubtitleWindow(QWidget):

    def __init__(self,audioClips_path,engine,parent=None):
        super(SubtitleWindow, self).__init__()
        self.audioClips_path=audioClips_path
        self.engine=engine
        self.parent=parent
        self.clip_dir_path='default'  #当前选择的clips目录,默认是default
        self.paragraph_list=[]  #当前拆分后的文本内容
        self.volume=1.0  #初始化音量大小
        self.rate=1.0    #初始化语速大小

        self.mid_font=QFont('',12,65)
        self.btn_style='''
                          QPushButton{color:black}
                          QPushButton:hover{color:red}
                          QPushButton{background-color:#FFDEAD}
                          QPushButton{border:2px}
                          QPushButton{border-radius:3px}
                          QPushButton{padding:1px 1px}
                        '''

        self.setWindowTitle('文本转音频')
        icon_path=os.path.join(os.path.dirname(os.path.dirname(__file__)),'imgs','cgai.png')
        self.icon=QIcon(icon_path)
        self.setWindowIcon(self.icon)
        self.main_layout=QHBoxLayout()

        self.left_layout=QVBoxLayout()
        # self.left_layout.setAlignment(Qt.AlignCenter)
        self.audio_list_layout=QVBoxLayout()
        self.audio_list_layout.setAlignment(Qt.AlignCenter)
        self.audio_label=QLabel('音频项目列表')
        self.audio_label.setFont(self.mid_font)
        self.audio_label.setAlignment(Qt.AlignCenter)
        self.audio_list=AudioClipList(self.audioClips_path)
        self.audio_list.clip_dir_Signal.connect(self._show_clips)
        self.audio_list_layout.addWidget(self.audio_label)
        self.audio_list_layout.addWidget(self.audio_list)
        self.attr_layout=QVBoxLayout()
        self.attr_label=QLabel('音频设置')
        self.attr_label.setFont(self.mid_font)
        self.attr_label.setAlignment(Qt.AlignCenter)
        self.volume_layout=QHBoxLayout()
        self.volume_label=QLabel('音量系数:')
        self.volume_slider=QSlider(Qt.Horizontal)
        self.volume_layout.addWidget(self.volume_label)
        self.volume_layout.addWidget(self.volume_slider)
        self.rate_layout=QHBoxLayout()
        self.rate_label=QLabel('语速系数:')
        self.rate_slider=QSlider(Qt.Horizontal)
        self.init_slider()    #初始化2个slider
        self.rate_layout.addWidget(self.rate_label)
        self.rate_layout.addWidget(self.rate_slider)
        self.attr_layout.addWidget(self.attr_label)
        self.attr_layout.addLayout(self.volume_layout)
        self.attr_layout.addLayout(self.rate_layout)
        self.left_layout.addLayout(self.audio_list_layout)
        self.left_layout.addLayout(self.attr_layout)

        self.mid_layout=QVBoxLayout()
        # self.mid_layout.setSpacing(25)
        self.text_label=QLabel('文本区域')
        self.text_label.setFont(self.mid_font)
        self.text_label.setAlignment(Qt.AlignCenter)
        self.text_edit=SubtitleText()
        self.text_edit.setMinimumWidth(500)
        self.text_edit.setMinimumHeight(800)
        self.text_edit.setPlaceholderText('这里输入字幕，按~键会自动添加分段结尾[<end>]`,注意不要用中文的~输入,否则不会触发')
        self.mark_btn_layout=QHBoxLayout()
        self.mark_btn_layout.setContentsMargins(0,20,0,0)
        self.end_btn=QPushButton('标记结尾')
        self.end_btn.setStyleSheet(self.btn_style)
        self.end_btn.clicked.connect(self._mark_end)
        self.auto_btn=QPushButton('自动标记')
        self.auto_btn.setStyleSheet(self.btn_style)
        self.auto_btn.clicked.connect(self._auto_mark)
        self.mark_btn_layout.addWidget(self.end_btn)
        self.mark_btn_layout.addWidget(self.auto_btn)
        self.audio_btn_layout=QHBoxLayout()
        self.audio_btn_layout.setContentsMargins(0, 17, 0, 2)
        self.audio_test_btn=QPushButton('测试音频')
        self.audio_test_btn.setStyleSheet(self.btn_style)
        self.audio_test_btn.clicked.connect(self._test_volume)
        self.create_btn=QPushButton('生成音频')
        self.create_btn.setStyleSheet(self.btn_style)
        self.create_btn.clicked.connect(self._convert_audioclips)
        self.audio_btn_layout.addWidget(self.audio_test_btn)
        self.audio_btn_layout.addWidget(self.create_btn)

        self.mid_layout.addWidget(self.text_label)
        self.mid_layout.addWidget(self.text_edit)
        self.mid_layout.addLayout(self.mark_btn_layout)
        self.mid_layout.addLayout(self.audio_btn_layout)


        self.right_layout=QVBoxLayout()
        self.clip_label=QLabel('音频列表')
        self.clip_label.setFont(self.mid_font)
        self.clip_label.setAlignment(Qt.AlignCenter)
        self.clips_list=QListWidget()
        self.clips_list.setDragEnabled(True)
        self.clips_list.doubleClicked.connect(self._play_audioclip)
        # self.text_connect=QTextBrowser()
        self.right_layout.addWidget(self.clip_label)
        self.right_layout.addWidget(self.clips_list)
        # self.right_layout.addWidget(self.text_connect)

        self.main_layout.addLayout(self.left_layout)
        self.main_layout.addLayout(self.mid_layout)
        self.main_layout.addLayout(self.right_layout)
        self.setLayout(self.main_layout)

        self._show_clips(self.clip_dir_path)
        #计时器
        self.timer=QBasicTimer()
        #进度条
        self.prog=ConvertProgress()
        #播放器
        # self.play = PlayThread(self.engine, 'start')

    def init_slider(self):
        """
        初始化volume_slider
        :return:
        """
        self.volume_slider.setMinimum(-10)
        self.volume_slider.setMaximum(10)
        self.volume_slider.setValue(0)
        self.volume_slider.valueChanged.connect(self._volume_changed)


        self.rate_slider.setMinimum(-80)
        self.rate_slider.setMaximum(80)
        self.rate_slider.setValue(0)
        self.rate_slider.valueChanged.connect(self._rate_changed)

    def _volume_changed(self,value):
        self.volume=value*0.1
        # volume = self.engine.getProperty('volume')
        self.engine.setProperty('volume', 1.0+self.volume)

    def _rate_changed(self,value):
        self.rate = value
        # rate = self.engine.getProperty('rate')
        self.engine.setProperty('rate', 200+self.rate)

    def timerEvent(self, *args, **kwargs):
        # percent=int(self._thread.count*100.0/self.frames)
        self.prog.setValue(self._thread.percent)
        if self._thread.percent==100:
            self.prog.close()
            self.timer.stop()
            QMessageBox.information(None,'提示','完成')
            self._show_clips(self.clip_dir_path)
            if self.parent:
                self.parent._show_clips(self.clip_dir_path)

    def _mark_end(self):
        tc=self.text_edit.textCursor()
        tc.insertText('[<end>]`')

    def _auto_mark(self):
        text = self.text_edit.toPlainText()
        if text:
            cut_len = 33
            all_len = len(text)
            max_num = int(all_len / 33)

            clips = []
            for i in range(max_num):
                clip = text[cut_len * i:cut_len * (i + 1)]
                clips.append(clip)
                print(clip)

            max_len = max_num * cut_len
            if max_len < all_len:  # 当小于总长度时
                last = text[max_len:]
                print(last)
                clips.append(last)

            last_text = ''
            for c in clips:
                last_text += c + '[<end>]`'
            self.text_edit.setText(last_text)


    def _test_volume(self):
        text=self.text_edit.toPlainText()
        if text:
            self.engine.say(text)  #现阶段只能卡屏幕播放了，因为多线程播放会崩溃
            self.engine.runAndWait()


    def _convert_audioclips(self):
        text=self.text_edit.toPlainText()
        if text:
            if self.audio_list.currentItem() and self.audio_list.currentItem().text()!='bgm':  #非bgm文件夹
                self.paragraph_list=text.split('[<end>]`')
                if self.paragraph_list:
                    clips_path=os.path.join(self.audioClips_path,self.clip_dir_path)
                    print('clips_path:',clips_path)
                    ori_dirname =sorted(os.listdir(clips_path))  #原先存在的目录索引
                    max_dir=0
                    if ori_dirname:
                        max_dir=int(ori_dirname[-1])+1
                    self._thread=ConvertAudipClip(self.engine,clips_path,self.paragraph_list,max_dir)
                    self.prog.show()
                    # self.prog.move(self.parent().width()/2+self.parent().pos().x()-self.prog.width()/2, self.parent.height()/2+self.parent.pos().y()-self.prog.height()/2)
                    self.timer.start(10, self)
                    self._thread.start()
            else:   #当前选择的是bgm是
                QMessageBox.information(None,'提示','请选择非bgm的音频目录')

    def update_clip_list(self,clip_data):
        """
        更新右侧的audio列表
        :param clip_data: {'0':{'mp3':mp3_path,'txt':txt_path},'1':{'mp3':mp3_path,'txt':txt_path}}
        :return:
        """
        self.clips_list.clear()
        # if not isbgm:
        temp_key_list=sorted(clip_data.keys(),key=lambda x:int(x))
        for k in temp_key_list:
            v=clip_data[k]
            txt_path=v['txt']
            data={}
            text=k
            if txt_path:  #如果没有txt_path表示当前选择的是bgm目录
                with open(txt_path,'r',encoding='utf8') as r:
                    data=eval(r.read())
                # print('data:',data)
                subtitle=data['subtitle']
                text=k+'.'+subtitle
            # data['mp3'] = v['mp3']
            # data['txt'] = v['txt']
            item=QListWidgetItem()
            item.setText(text)
            item.setTextAlignment(Qt.AlignLeft)
            item.setData(5,data)
            self.clips_list.addItem(item)

        # else:  #当选择的是bgm时
        #     for b,d in clip_data.items():
        #         data={}
        #         data['subtitle']=''   #如果选择的是背景音乐那么就不读
        #         text=b
        #         item = QListWidgetItem()
        #         item.setText(text)
        #         item.setTextAlignment(Qt.AlignLeft)
        #         item.setData(5, data)
        #         self.clips_list.addItem(item)


    def _show_clips(self,clip_dir_name):
        """
        选择的音频小目录并显示每个字幕内容。
        /audioClips/1/clip.mp3,clip.txt

        :param clip_dir_name:
        :return:
        """
        self.clip_dir_path = os.path.join(self.audioClips_path, clip_dir_name)
        clips_dirs = os.listdir(self.clip_dir_path)  # 这里列出的是每个按索引序列的文件夹
        clip_data = {}
        if clips_dirs:
            for i in clips_dirs:
                clip_d = os.path.join(self.clip_dir_path, i)
                mp3_path = os.path.join(clip_d, 'clip.mp3')
                txt_path = os.path.join(clip_d, 'clip.txt')
                clip_data[i] = {'mp3': mp3_path, 'txt': txt_path}  # 存入每个clip目录下对应的mp3与txt信息

            if clip_data:
                try:
                    self.update_clip_list(clip_data)
                except Exception as update_ERR:
                    print(str(update_ERR))
            # if clip_dir_name != 'bgm':
            #     for i in clips_dirs:
            #         clip_d=os.path.join(self.clip_dir_path,i)
            #         mp3_path=os.path.join(clip_d,'clip.mp3')
            #         txt_path=os.path.join(clip_d,'clip.txt')
            #         clip_data[i]={'mp3':mp3_path,'txt':txt_path}  #存入每个clip目录下对应的mp3与txt信息
            #
            #     if clip_data:
            #         try:
            #             self.update_clip_list(clip_data,False)
            #         except Exception as update_ERR:
            #             print(str(update_ERR))
            #
            # else:  #当时选择的是bgm目录时，clips_dirs就是所有的bgm文件
            #     for i in clips_dirs:  #此时的i不再是目录名字，而是bgm的音乐名字
            #         mp3_path=os.path.join(self.clip_dir_path,i)
            #         txt_path=''
            #         clip_data[i]={'mp3':mp3_path,'txt':txt_path}
            #
            #     if clip_data:
            #         try:
            #             self.update_clip_list(clip_data,True)
            #         except Exception as update_ERR:
            #             print(str(update_ERR))

    def _play_audioclip(self,index):
        """
        打开音频
        :param index:
        :return:
        """
        # print(index)
        item=self.clips_list.itemFromIndex(index)
        data=item.data(5)
        subtitle=data['subtitle']

        if subtitle:
            self.engine.say(subtitle)  #现阶段只能卡屏幕播放了，因为多线程播放会崩溃
            self.engine.runAndWait()



if __name__ == '__main__':
    engine = pyttsx3.init()
    rate = engine.getProperty('rate')
    engine.setProperty('rate', rate - 50)
    volume = engine.getProperty('volume')
    engine.setProperty('volume', volume + 0.55)
    engine.setProperty('voice', 'zh')

    app=QApplication(sys.argv)
    ap=r'D:\Q\TimeLineOut\myFirstProject\audioClips'
    s=SubtitleWindow(ap,engine)
    s.show()
    sys.exit(app.exec_())